package main

import (
	"bufio"
	"fmt"
	"golang.org/x/net/html"
	"io"
	"log"
	"net/http"
	"os"
	"time"
	"./util"
)


func main() {
	//t_error_handling()
	t_findlinks2() // go run app05.go https://www.baidu.com 貌似没有任何输出，用163.com可以
	//t_findlink() //curl www.baidu.com | go run app05.go
	//t_outline()
}

func visit(links []string, n *html.Node) []string {
	if n.Type == html.ElementNode && n.Data == "a" {
		for _, a := range n.Attr {
			if a.Key == "href" {
				links = append(links, a.Val)
			}
		}
	}
	for c := n.FirstChild; c != nil; c = c.NextSibling {
		links = visit(links, c)
	}
	return links
}

func t_findlink() {
	doc, err := html.Parse(os.Stdin)
	if err != nil {
		fmt.Fprintf(os.Stderr, "findlink1: $v\n", err)
		os.Exit(1)
	}
	i := 0
	for _, link := range visit(nil, doc) {
		i = i + 1
		//fmt.Println(i)
		fmt.Println(link)
	}
}

func outline(stack [] string, n *html.Node) {
	if n.Type == html.ElementNode {
		stack = append(stack, n.Data)
		fmt.Println(stack)
	}
	for c := n.FirstChild; c != nil; c = c.NextSibling {
		outline(stack, c)
	}
}

// 用递归打印html tree的结构
func t_outline() {
	doc, err := html.Parse(os.Stdin)
	if err != nil {
		fmt.Fprintf(os.Stderr, "findlink1: $v\n", err)
		os.Exit(1)
	}
	outline(nil, doc)
}

func findlinks(url string) ([]string, error) {
	resp, err := http.Get(url)
	if err != nil {
		return nil, err
	}
	if resp.StatusCode != http.StatusOK {
		resp.Body.Close()
		return nil, fmt.Errorf("getting %s: %s", url, resp.Status)
	}
	doc, err := html.Parse(resp.Body)
	resp.Body.Close() //释放网络资源
	if err != nil {
		// seldom happen except for underlying IO error
		return nil, fmt.Errorf("parsing %s as html : %v", url, err)
	}
	return visit(nil, doc), nil
}

//5.3 访问一个url下的所有link
func t_findlinks2() {
	for _, url := range os.Args[1:] {
		links, err := findlinks(url)
		if err != nil {
			fmt.Fprintf(os.Stderr, "findlink2: %v\n", err)
			continue
		}
		for _, link := range links {
			fmt.Println(link)
		}
	}
}

// error处理的第二种策略，重试(exponetial backoff)
func WaitForServer(url string) error {
	const timeout = 5 * time.Second;
	deadline := time.Now().Add(timeout)
	for tries := 0; time.Now().Before(deadline); tries++ {
		log.Println("begin get")
		_, err := http.Get(url) // Get 本身需要时间等待，如果设置的超时很短，重试可能起不到作用
		log.Println("end get")
		if err == nil {
			return nil
		}
		log.Printf("server not responding (%s); retrying ...", err)
		time.Sleep(time.Second << uint(tries)) // exponential back-off ?
	}
	return fmt.Errorf("server %s failed to respond after %s", url, timeout)
}

func t_error_handling() {
	if err := WaitForServer(os.Args[1]); err != nil {
		fmt.Fprintf(os.Stderr, "site is down: %v\n", err)
		os.Exit(1)
	}
}

// 如果遇到EOF，则跳出循环
func t_eof() {
	in := bufio.NewReader(os.Stdin)
	for {
		_, _, err := in.ReadRune()
		if err == io.EOF {
			log.Println("eof reaches")
			break
		}
		if err != nil {
			log.Fatalf("ReadRune error: %v", err)
		}
	}
}